package com.acmedcare.framework.exchange.center.jmx;

import com.acmedcare.framework.exchange.center.core.PluginContainerMBean;
import com.acmedcare.framework.exchange.center.core.adapter.DefaultAdapterResolver;
import com.acmedcare.framework.exchange.center.core.agent.DefaultAgentResolver;
import com.acmedcare.framework.exchange.center.core.application.RemoteApplicationResolver;
import com.acmedcare.framework.exchange.center.core.consumer.DefaultConsumerResolver;
import com.acmedcare.framework.exchange.center.core.handler.AgentHandler;
import com.acmedcare.framework.exchange.center.core.handler.ConsumerHandler;
import com.acmedcare.framework.exchange.center.core.handler.IAgentPublisher;
import com.acmedcare.framework.exchange.center.core.handler.PluginHandler;
import com.acmedcare.framework.exchange.center.core.resolve.DefaultPluginBuilder;
import com.acmedcare.framework.exchange.center.entity.dto.Hdfs;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

// import org.springframework.amqp.rabbit.core.RabbitTemplate;

@Slf4j
public class JmxServer {

  /**
   * Date格式化字符串
   */
  public static final String DATE_FORMAT = "yyyy-MM-dd";
  /**
   * DateTime格式化字符串
   */
  public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
  /**
   * Time格式化字符串
   */
  public static final String TIME_FORMAT = "HH:mm:ss";

  protected static PluginContainerMBean containerMBean;

  protected static AgentHandler agentHandler;

  private static Channel channel;


  public static void main(String[] args) throws Exception {
    if (args.length < 2) {
      throw new Exception("启动参数个数正确，请确认参数后再试!");
    }

    log.info(String.format("接受启动参数[0]:%s,[1]:%s", args[0], args[1]));
    String pluginName = args[0];
    Integer port = Integer.parseInt(args[1]);

    ObjectMapper objectMapper = new ObjectMapper();
    configObjectMapper(objectMapper);

    containerMBean = new DefaultPluginBuilder(objectMapper)
        .addAgentResolver(new DefaultAgentResolver())
        .addApplicationResolver(new RemoteApplicationResolver(getHDFSConfig(pluginName)))
        .addConsumerResolver(new DefaultConsumerResolver())
        .addAdapterResolver(new DefaultAdapterResolver())
        .build();

    channel = RabbitUtils.getRabbitChannel();
    AMQP.BasicProperties props =
        new AMQP.BasicProperties("text/plain",
            "UTF-8",
            null,
            2,
            0, null, null, null,
            null, null, null, null,
            null, null);
    IAgentPublisher agentPublisher = (exchangeName, obj) -> {
      String dataString = "";
      try {

        dataString = objectMapper.writeValueAsString(obj);
        channel.exchangeDeclare(exchangeName, "fanout",true);
        channel.basicPublish(exchangeName, "", props, dataString.getBytes());
      } catch (Exception e) {

        log.error(String
            .format("交换器:%s，消息:%s,发送失败,异常信息：%s", exchangeName, dataString, e.getMessage()));
      }
    };

    MBeanServer server = ManagementFactory.getPlatformMBeanServer();
    ObjectName objectName = new ObjectName("jmxBean:name=pluginContainer");
    //plugin container
    server.registerMBean(containerMBean, objectName);
    //agent handler
    agentHandler = new AgentHandler(containerMBean, objectMapper, agentPublisher);
    server.registerMBean(agentHandler,
        new ObjectName("jmxBean:name=agentHandler"));
    //consumer handler
    server.registerMBean(new ConsumerHandler(objectMapper, containerMBean, agentPublisher),
        new ObjectName("jmxBean:name=consumerHandler"));
    //plugin handler
    server.registerMBean(new PluginHandler(objectMapper, containerMBean),
        new ObjectName("jmxBean:name=pluginHandler"));

    LocateRegistry.createRegistry(port);
    String uri = String.format("service:jmx:rmi:///jndi/rmi://127.0.0.1:%s/jmxrmi", port);
    log.info("当前链接地址为:" + uri);
    JMXServiceURL jmxServiceURL = new JMXServiceURL(uri);
    JMXConnectorServer cs = JMXConnectorServerFactory
        .newJMXConnectorServer(jmxServiceURL, null, server);
    cs.start();
    log.info(String.format("插件:%s,已启动.....", pluginName));
  }


  private static Hdfs getHDFSConfig(String name) {

    Hdfs hdfs = new Hdfs();
    hdfs.setUser("root");
    hdfs.setRootPath(String.format("./.temp/%s/jars", name));
    hdfs.setUri("hdfs://hadoop.acmedcare.com:9000");
    return hdfs;
  }

  private static void configObjectMapper(ObjectMapper objectMapper) {

    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    jackson2ObjectMapperBuilderCustomizer().customize(builder);
    builder.configure(objectMapper);
  }

  private static Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    //noinspection Duplicates
    return builder -> builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(
        DateTimeFormatter.ofPattern(DATETIME_FORMAT)))
        .serializerByType(LocalDate.class,
            new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
        .serializerByType(LocalTime.class,
            new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT)))
        .deserializerByType(LocalDateTime.class,
            new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)))
        .deserializerByType(LocalDate.class,
            new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
        .deserializerByType(LocalTime.class,
            new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
  }
}
